#pragma once
#include"sock.h"
#include<sys/select.h>
#include<sys/time.h>
#include<sys/select.h>
#include<unistd.h>

namespace ns_server
{
    using namespace ns_sock;
    
    static const int g_default=8080;  //默认端口号
    static const int num=sizeof(fd_set);
    class SelectServer
    {
    private:
        u_int16_t port_;
        int listen_sock_;

        //一个存放文件描述符以备更新set的数组
        int fd_array_[num];
        //增加一个数组有效最长长度-- 添加这个以防遍历完数组
        int size;
    public:
        SelectServer(int port=g_default): port_(port),listen_sock_(-1),size(0)
        {
            //给数组设置初始值--为-1
            for(int i=0;i<num;i++)
            {
                fd_array_[i]=-1;
            }
        }
        //初始化服务器
        void InitServer()
        {   
            listen_sock_=Sock::Socket();
            Sock::Bind(listen_sock_,port_);
            Sock::Listen(listen_sock_);
        }
        void PrintArray()
        {
            for(int i=0;i<size;i++)
            {
                if(fd_array_[i]==-1)
                    continue;
                std::cout<<fd_array_[i]<<" ";
            }
            std::cout<<std::endl;
        }
        //运行服务器
        void Loop()
        {
            //把lsiten_sock_添加进数组中，以便检测
            fd_array_[0]=listen_sock_;
            size=1;
            while(true)
            {
                PrintArray();
                //从数组中设置值
                fd_set read;
                FD_ZERO(&read);  //清空set
                //存储最大值--因为数组不连续，也非排序
                int max=-1;

                for(int i=0;i<size;i++)
                {
                    if(-1==fd_array_[i])
                        continue;
                    FD_SET(fd_array_[i],&read);
                    if(max<fd_array_[i])
                        max=fd_array_[i];
                }
                //timeout设置为nullptr，为阻塞
                int n=select(max+1,&read,nullptr,nullptr,nullptr); 
                switch(n)
                {
                    case 0:
                        //select返回0，时间到没有就绪文件
                        std::cout<<"time out...."<<std::endl;
                        break;
                    case -1:
                        //发生未知错误
                        std::cerr<<"select error!"<<std::endl;
                        break;
                    default:
                        //select成功，至少有一个就绪
                        //直接传位图进去，可处理多个文件就绪
                        HandlerEvent(read);
                        break;
                }
            }
        }
        void HandlerEvent(const fd_set& rfds)
        {
            //判断哪些文件就绪了
            for(int i=0;i<size;i++)
            {
                if(fd_array_[i]==-1)
                    continue;
                if(FD_ISSET(fd_array_[i],&rfds))
                {
                    //找到文件就绪
                    if(fd_array_[i]==listen_sock_)
                    {
                        //代表有新的链接到来--接收
                        struct sockaddr_in pre;
                        socklen_t len=sizeof(pre);
                        int fd=accept(listen_sock_,(sockaddr*)&pre,&len);
                        if(fd<0)
                        {
                            //accept失败
                            continue;
                        }
                        else
                        {
                            std::cout<<"accept: "<<fd<<std::endl;
                            //accept成功
                            //不能马上读取fd,因为客户端可能不会立马发数据
                            //添加进数组中
                            int i=0;
                            for(i=0;i<size;i++)
                            {
                                if(fd_array_[i]==-1)
                                {
                                    fd_array_[i]=fd;
                                    break;
                                }
                            }
                            if(i==size)
                            {
                                fd_array_[size]=fd;
                                size++;
                            }
                        }
                    }
                    else
                    {
                        //代表有其他文件内容到来
                        char buffer[1024];
                        buffer[0]=0;
                        //粘包问题并没有解决
                        int s=read(fd_array_[i],buffer,sizeof(buffer));
                        switch(s)
                        {
                            case 0:
                                //对端关闭   ---底层会接受报文，报文标志位为FIN,所以也算数据就绪
                                std::cout<<"client quit"<<std::endl;
                                //删除数组中这个文件的描述符
                                close(fd_array_[i]);
                                fd_array_[i]=-1;
                                if(i==size)
                                    size--;
                                break;
                            case -1:
                                std::cerr<<"read error"<<std::endl;
                                break;
                            default:
                                //s>0
                                buffer[s-1]=0;
                                std::cout<<"client say: "<<buffer<<std::endl;
                        }
                    }
                }
            }
        }
        ~SelectServer()
        {
            if(listen_sock_>0)
            {
                close(listen_sock_);
            }
        }
    };
}